#include "../J2C_4-1/LinkedList.h"
#include "../J2C_4-1/Shape.h"
#include <cstdlib>
#include <string>

#define BOOST_TEST_MODULE LinkedListTest
#include <boost/test/unit_test.hpp>

struct Fixture
{
	LinkedList<int> intList;
    LinkedList<string> strList;
	LinkedList<Shape*> shapeList;

    Fixture() 
    {
		//detect_memory_leak(true);
        intList.PushBack(7);
        intList.PushBack(19);
        intList.PushBack(43);
        intList.PushBack(3);
        intList.PushBack(1045);
        intList.PushBack(7);
        intList.PushBack(1);
        strList.PushBack("Bent");
        strList.PushBack("Ole");
        strList.PushBack("Anders");
        strList.PushBack("Svend");
        strList.PushBack("Peter");
		shapeList.PushBack(new Rectangle(100, 120, 50, 60));
		shapeList.PushBack(new Circle(10, 50, 100));
    } 
};

BOOST_AUTO_TEST_CASE(testLength)
{
	/*Fixture f;
	BOOST_CHECK_EQUAL(f.intList.Length(), 7);
	f.intList.PushBack(1337);
	BOOST_CHECK_EQUAL(f.intList.Length(), 8);
	f.intList.Clear();
	BOOST_CHECK_EQUAL(f.intList.Length(), 0);*/
}

BOOST_AUTO_TEST_CASE(testGet)
{
    Fixture f;
    BOOST_CHECK_EQUAL(f.intList.Get(0), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(1), 19);
    BOOST_CHECK_EQUAL(f.intList.Get(2), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(4), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(5), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(6), 1);
	BOOST_CHECK_EQUAL(f.strList.Get(0), "Bent");
	BOOST_CHECK_EQUAL(f.strList.Get(1), "Ole");
	BOOST_CHECK_EQUAL(f.strList.Get(2), "Anders");
	BOOST_CHECK_EQUAL(f.strList.Get(3), "Svend");
	BOOST_CHECK_EQUAL(f.strList.Get(4), "Peter");
	try							{ f.intList.Get(7); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
}

BOOST_AUTO_TEST_CASE(testClear)
{
	Fixture f;
	f.intList.Clear();
	BOOST_CHECK_EQUAL(f.intList.Length(), 0);
	try							{ f.intList.Get(0); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
}

BOOST_AUTO_TEST_CASE(testGetNode)
{
	Fixture f;
	BOOST_CHECK_EQUAL(f.intList.GetNode(0)->content, 7);
	BOOST_CHECK_EQUAL(f.intList.GetNode(1)->content, 19);
    BOOST_CHECK_EQUAL(f.intList.GetNode(2)->content, 43);
    BOOST_CHECK_EQUAL(f.intList.GetNode(3)->content, 3);
    BOOST_CHECK_EQUAL(f.intList.GetNode(4)->content, 1045);
    BOOST_CHECK_EQUAL(f.intList.GetNode(5)->content, 7);
    BOOST_CHECK_EQUAL(f.intList.GetNode(6)->content, 1);
	BOOST_CHECK_EQUAL(f.strList.GetNode(0)->content, "Bent");
	BOOST_CHECK_EQUAL(f.strList.GetNode(1)->content, "Ole");
	BOOST_CHECK_EQUAL(f.strList.GetNode(2)->content, "Anders");
	BOOST_CHECK_EQUAL(f.strList.GetNode(3)->content, "Svend");
	BOOST_CHECK_EQUAL(f.strList.GetNode(4)->content, "Peter");
}

BOOST_AUTO_TEST_CASE(testErase)
{
	Fixture f;
	f.intList.Erase(1);
	f.intList.Erase(2);
	BOOST_CHECK_EQUAL(f.intList.Get(0), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(1), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(2), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(4), 1);
	BOOST_CHECK_EQUAL(f.intList.Length(), 5);
}

BOOST_AUTO_TEST_CASE(testInsert)
{
	Fixture f;
	int l = f.intList.Length();
	f.intList.Insert(0,0);
	f.intList.Insert(3, 7327);
	BOOST_CHECK_EQUAL(f.intList.Get(0), 0);
	BOOST_CHECK_EQUAL(f.intList.Get(1), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(2), 19);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 7327);
	BOOST_CHECK_EQUAL(f.intList.Get(4), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(5), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(6), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(7), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(8), 1);
	try							{ f.intList.Insert(10,0); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
	BOOST_CHECK_EQUAL(f.intList.Length(), l+2);
}

BOOST_AUTO_TEST_CASE(testPushFront)
{
	Fixture f;
	int l = f.intList.Length();
	f.intList.PushFront(13);
	f.intList.PushFront(14);
	BOOST_CHECK_EQUAL(f.intList.Get(0), 14);
	BOOST_CHECK_EQUAL(f.intList.Get(1), 13);
	BOOST_CHECK_EQUAL(f.intList.Get(2), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 19);
	BOOST_CHECK_EQUAL(f.intList.Get(4), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(5), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(6), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(7), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(8), 1);
	try							{ f.intList.Insert(10,0); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
	BOOST_CHECK_EQUAL(f.intList.Length(), l+2);
}

BOOST_AUTO_TEST_CASE(testPushBack)
{
	Fixture f;
	int l = f.intList.Length();
	f.intList.PushBack(13);
	f.intList.PushBack(14);
	BOOST_CHECK_EQUAL(f.intList.Get(0), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(1), 19);
	BOOST_CHECK_EQUAL(f.intList.Get(2), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(4), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(5), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(6), 1);
	BOOST_CHECK_EQUAL(f.intList.Get(7), 13);
	BOOST_CHECK_EQUAL(f.intList.Get(8), 14);
	try							{ f.intList.Insert(10,0); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
	BOOST_CHECK_EQUAL(f.intList.Length(), l+2);
}

BOOST_AUTO_TEST_CASE(testPopFront)
{
	Fixture f;
	int l = f.intList.Length();
	BOOST_CHECK_EQUAL(f.intList.PopFront(), 7);
	BOOST_CHECK_EQUAL(f.intList.PopFront(), 19);

	BOOST_CHECK_EQUAL(f.intList.Get(0), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(1), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(2), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(4), 1);
	BOOST_CHECK_EQUAL(f.intList.Length(), l-2);

	f.intList.Clear();
	try							{ f.intList.PopFront(); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
}

BOOST_AUTO_TEST_CASE(testPopBack)
{
	Fixture f;
	int l = f.intList.Length();
	BOOST_CHECK_EQUAL(f.intList.PopBack(), 1);
	BOOST_CHECK_EQUAL(f.intList.PopBack(), 7);

	BOOST_CHECK_EQUAL(f.intList.Get(0), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(1), 19);
	BOOST_CHECK_EQUAL(f.intList.Get(2), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(4), 1045);
	BOOST_CHECK_EQUAL(f.intList.Length(), l-2);

	f.intList.Clear();
	try							{ f.intList.PopBack(); }
	catch(std::out_of_range e)	{ BOOST_CHECK_EQUAL(e.what(), "Index out of bounds"); }
}

BOOST_AUTO_TEST_CASE(testSwap)
{
	Fixture f;
	f.intList.Swap(f.intList.GetNode(1), f.intList.GetNode(3));
	f.intList.Swap(0, 6);
	BOOST_CHECK_EQUAL(f.intList.Get(0), 1);
    BOOST_CHECK_EQUAL(f.intList.Get(1), 3);
    BOOST_CHECK_EQUAL(f.intList.Get(2), 43);
    BOOST_CHECK_EQUAL(f.intList.Get(3), 19);
    BOOST_CHECK_EQUAL(f.intList.Get(4), 1045);
    BOOST_CHECK_EQUAL(f.intList.Get(5), 7);
    BOOST_CHECK_EQUAL(f.intList.Get(6), 7);
}

BOOST_AUTO_TEST_CASE(testQuickSort)
{
	Fixture f;
	f.intList.QuickSort();
	for(int i=0; i<f.intList.Length()-1; i++)
		BOOST_CHECK(f.intList.Get(i) <= f.intList.Get(i+1));
	f.strList.QuickSort();
	for(int i=0; i<f.strList.Length()-1; i++)
		BOOST_CHECK(f.strList.Get(i) <= f.strList.Get(i+1));
}

BOOST_AUTO_TEST_CASE(testSelectionSort)
{
	Fixture f;
	f.intList.SelectionSort();
	for(int i=0; i<f.intList.Length()-1; i++)
		BOOST_CHECK(f.intList.Get(i) <= f.intList.Get(i+1));
	f.strList.SelectionSort();
	for(int i=0; i<f.strList.Length()-1; i++)
		BOOST_CHECK(f.strList.Get(i) <= f.strList.Get(i+1));
}

BOOST_AUTO_TEST_CASE(testIterator)
{
    Fixture f;
    LinkedList<int>::Iterator itr(f.intList.GetNode(0));
    int i = 0;
    for(itr = f.intList.begin(); itr != f.intList.end(); ++itr)
        BOOST_CHECK_EQUAL(*itr, f.intList.Get(i++));

	LinkedList<string>::Iterator strItr(f.strList.GetNode(0));
    i = 0;
    for(strItr = f.strList.begin(); strItr != f.strList.end(); ++strItr)
        BOOST_CHECK_EQUAL(*strItr, f.strList.Get(i++));
}

BOOST_AUTO_TEST_CASE(testPolymorphism)
{
	Fixture f;
	BOOST_CHECK_EQUAL(((Rectangle*)f.shapeList.Get(0))->X, 100);
	BOOST_CHECK_EQUAL(((Rectangle*)f.shapeList.Get(0))->Y, 120);
	BOOST_CHECK_EQUAL(((Rectangle*)f.shapeList.Get(0))->Width, 50);
	BOOST_CHECK_EQUAL(((Rectangle*)f.shapeList.Get(0))->Height, 60);
	BOOST_CHECK_EQUAL(((Circle*)f.shapeList.Get(1))->X, 10);
	BOOST_CHECK_EQUAL(((Circle*)f.shapeList.Get(1))->Y, 50);
	BOOST_CHECK_EQUAL(((Circle*)f.shapeList.Get(1))->Radius, 100);
}